/**
 * Created on 2018年5月30日 by hexin
 */
package com.ahdms.es.util;


import com.ahdms.es.gm.algorithm.SM2;
import com.ahdms.es.gm.algorithm.SM3Digest;
import com.ahdms.es.gm.asn1.generic.ECCPoint;
import com.ahdms.es.gm.asn1.generic.PublicMatrix;
import com.ahdms.es.gm.constant.IKIObjectIdentifiers;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.math.BigInteger;


/**
 * @author hexin
 * @version 1.0
 * @Title
 * @Description
 * @Copyright <p>
 * Copyright (c) 2018
 * </p>
 * @Company <p>
 * 迪曼森信息科技有限公司 Co., Ltd.
 * </p>
 * @修改记录
 * @修改序号，修改日期，修改人，修改内容
 */

public class IKIUtil {

    static Logger logger = LoggerFactory.getLogger(IKIUtil.class);



    /**
     * 通过公钥矩阵和标识计算标识公钥
     *
     * @param pkmCert  公钥矩阵
     * @param identity 标识
     * @return
     * @创建人 hexin
     * @创建时间 2018年10月18日
     * @创建目的【】
     * @修改目的【修改人：，修改时间：】
     */
    public static byte[] getPublicKeyFromPKM(Certificate pkmCert, String identity) {
        byte[] publicKey = new byte[132];//最终返回结果
        SubjectPublicKeyInfo publicKeyInfo = TBSCertificate.getInstance(pkmCert.getTBSCertificate()).getSubjectPublicKeyInfo();
        ASN1ObjectIdentifier algOid = publicKeyInfo.getAlgorithm().getAlgorithm();
        ASN1ObjectIdentifier paramOid = ASN1ObjectIdentifier.getInstance(publicKeyInfo.getAlgorithm().getParameters());

        if (IKIObjectIdentifiers.ecc_pub_key.getId().equals(algOid.getId()) && IKIObjectIdentifiers.sm_puk_matrix.getId().equals(paramOid.getId())) {
            SM2 sm2 = SM2.getInstance();
            ECCurve curve = sm2.getEccCurve();
            PublicMatrix pkm = PublicMatrix.getInstance(publicKeyInfo.getPublicKeyData().getBytes());

            SM3Digest sm3 = new SM3Digest();
            byte[] md = sm3.sm3Digest(identity.getBytes());
            int columns = pkm.getColumns().getValue().intValue();
            int rows = pkm.getRows().getValue().intValue();
            int ncolumnbits = pkm.getColumnbits().getValue().intValue();
            ASN1Sequence points = pkm.getEccPoints();

            int[] indexList = toColumnIndexList(md, rows, ncolumnbits);

            int index = indexList[0];
            ECCPoint point = ECCPoint.getInstance(points.getObjectAt(index));
            BigInteger xB = point.getX().getPositiveValue();
            BigInteger yB = point.getY().getPositiveValue();
            ECPoint pkPoint = curve.createPoint(xB, yB);

            for (int i = 1; i < rows; i++) {
                index = i * columns + indexList[i];
                point = ECCPoint.getInstance(points.getObjectAt(index));
                xB = point.getX().getPositiveValue();
                yB = point.getY().getPositiveValue();
                pkPoint = pkPoint.add(curve.createPoint(xB, yB));
            }

            byte[] pkPointXB = pkPoint.getX().getEncoded();
            byte[] pkPointYB = pkPoint.getY().getEncoded();

            return ByteUtils.mergeBytes(pkPointXB, pkPointYB);


        } else {
            return null;
        }

    }

    private static int[] toColumnIndexList(byte[] data, int nrows, int ncolumnbits) {
        int bitLen = ncolumnbits * nrows;
        //int byteLen = bitLen / 8;
        int byteLen = bitLen >> 3;
        int[] indexList = new int[nrows];

        //byte 转 二进制字符串
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < byteLen; i++) {
            sb.append(byteToBinaryString(data[i]));
        }
        String bitStr = sb.toString();

        for (int i = 0; i < nrows; i++) {
            //反序
            indexList[nrows - i - 1] = Integer.parseInt(bitStr.substring(i * ncolumnbits, (i + 1) * ncolumnbits), 2);
        }
        return indexList;
    }

    private static String byteToBinaryString(byte b) {
        String tmpStr = Integer.toBinaryString(b & 0xff);
        if (8 == tmpStr.length()) {
            return tmpStr;
        } else {
            int paddingLength = 8 - tmpStr.length();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < paddingLength; i++) {
                sb.append("0");
            }
            sb.append(tmpStr);
            return sb.toString();
        }
    }

}
